-- Incidente reportado por Lalo Bece.
-- Para los comprobantes fiscales que tienen comm.afipcbtetipo, incluimos en la busqueda de iguales el codigo de subdiario (com.codctbsub) para
-- distinguir entre comprobantes de compra y de venta, porque decia que el comprobante de venta estaba repetido, cuando en verdad el numero pertenecia a uno de compra.


CREATE OR REPLACE FUNCTION public.veri_uni(
    integer,
    character varying,
    integer,
    character varying,
    integer,
    integer,
    character varying,
    character varying,
    character varying,
    timestamp without time zone,
    integer,
    integer)
  RETURNS boolean AS
$BODY$
DECLARE
pIdComm 		ALIAS FOR $1;
pTipoCom 		ALIAS FOR $2;
pIdPad 			ALIAS FOR $3;
pLetra 			ALIAS FOR $4;
pNroPtoVta 		ALIAS FOR $5;
pNro 			ALIAS FOR $6;
pCodAmbSuc 		ALIAS FOR $7;
pNombreAmbEqu 		ALIAS FOR $8;
pTipoCai 		ALIAS FOR $9;
pFecha 			ALIAS FOR $10;
pIdComTal 		ALIAS FOR $11;
pAfipCbteTipo 		ALIAS FOR $12;

vTipoUnicidad 		Com.TipoUnicidad%TYPE;
vContador 		INTEGER;
vCuit 			Pad.Cuit%TYPE;
vCodComTal 		ComTal.Cod%TYPE;
vFechaInicioControlNumeracion ComTal.FechaInicioControlNumeracion%TYPE;
vCodAmbEmp 		VARCHAR;
vSucsDeMiEmpresa 	VARCHAR;
vSucsFisDeMiEmpresa 	VARCHAR;
vEsComprobanteFiscal	BOOLEAN;

BEGIN
-- Para probar: select veri_uni(cast(null as integer), 'FV', 1000, 'A', 1, 2000, 'a1', 'a0', 'PCEDU', 'W', cast('2019-11-25' as timestamp without time zone), cast(null as integer), 1);


/* comm.tipocai  = 'W' y comm.nro = 0, estamos ante el caso de un comprobante electronico numerado por web service.   */
/* Si es numerado por web service (facturacion electronica), el numero cero significa pendiente de numerar, puede haber muchos y no se controla su repeticion  */
IF pTipoCai = 'W' AND  pNro = 0 THEN
	RETURN TRUE; 
END IF;

/* comm.tipocai  = 'F' y comm.nro = 0, estamos ante el caso de un comprobante fiscal generado UNICAMENTE por gestionWeb. */
/* Si es numerado por impresor fiscal, el numero cero significa pendiente de numerar, puede haber muchos y no se controla su repeticion  */
IF pTipoCai = 'F' AND  pNro = 0 THEN
	RETURN TRUE; 
END IF;

/* TipoUnicidad = 'N': No controla la repeticion de la numeracion de este tipo de comprobante */

SELECT TipoUnicidad INTO vTipoUnicidad FROM Com WHERE Tipo = pTipoCom;

IF vTipoUnicidad = 'N' OR vTipoUnicidad IS NULL THEN
	RETURN TRUE; 
END IF;


/*

CONSIDERACIONES ACERCA DE LA UNICIDAD DE COMPROBANTES (Noviembre de 2019):

Respecto a este tema, definimos aqui dos supertipos de comprobantes:

1 - Comprobantes fiscales: Son aquellos cuyo comm.afipcbtetipo es no nulo (tiene su correspondencia en afip) y la sucursal que lo emite es fiscal (no es repositorio, vendedores, plantillas para copia de comprobantes, etc.).
	    Aclaracion: comm.afipcbtetipo tiene valor solo cuando ese tipocom//letra tiene una equivalencia en public.afipeqvcom para idafipeqv = ambcfg.idafipeqv (equivalencias afip).
			Por ejemplo, un recibo de cobro o un remito, seguramente no sera un comprobante fiscal.
2 - Comprobantes no fiscales: Todos los que no cumplen la condicion (1).

Reglas:

1 - Si el comprobante es Fiscal, la unicidad sera por: empresa//comm.afipcbtetipo//comm.nroptovta//comm.nro
2 - Si el comprobante es no fiscal, el control de unicidad sera como hasta ahora (por talonario, comm.tipocom, comm.letra, etc.).


En ambos casos se debera considerar el tipo de control de repeticion, pero por caminos diferentes, lo que obliga a una doble programacion de esta funcion.
Los controles de repeticion, definidos por tipo de comprobante, pueden ser:
	- No controla.
	- Controla para los del mismo talonario.
	- Controla para los de la entidad.
	- Controla para esa entidad y las que tienen la misma cuit.
	- Controla para los de la misma entidad y fecha.

La diferencia esta en que en los comprobantes no fiscales tiene incidencia el talonario, mientras que en los comprobantes fiscales el talonario es reemplazado por el campo comm.afipcbtetipo.
El control de unicidad es independiente del mecanismo de emision:
	Da lo mismo si se trata de una factura electronica emitida con el sistema mediante el webService de afip, o si es una factura de compra recibida de un proveedor.

Nota: seguramente se deba agregar un indice a la tabla comm, para que el acceso por comm.afipcbtetipo sea rapido. Probarlo en una base grande.

*/

/* Recupero el codigo de empresa. */

vCodAmbEmp:= (SELECT codambemp FROM ambempsuc WHERE codambsuc = pCodAmbSuc ORDER BY codambsuc LIMIT 1);


/* Recupero las sucursales de la empresa. */
vSucsDeMiEmpresa:= sucursales_de_emp(vCodAmbEmp);
vSucsFisDeMiEmpresa:= sucursales_de_ung(vCodAmbEmp, (select fiscodambung from ambemp where cod = vCodAmbEmp limit 1), (select fiscodambsuc from ambemp where cod = vCodAmbEmp limit 1));
vEsComprobanteFiscal:= pAfipCbteTipo IS NOT NULL AND INDEX(vSucsFisDeMiEmpresa, pCodAmbSuc) > 0;    -- Si tiene codigo afip y la sucursal es fiscal.

/* NOTA PARA EL IMPLEMENTADOR: Si queremos que controle la unicidad como antes de Noviembre de 2019, habilitar la siguiente linea:  */
--  vEsComprobanteFiscal:= FALSE;


/* TipoUnicidad = 'E': Controla que el numero no se repita dentro en una misma entidad */
IF vTipoUnicidad = 'E' THEN
	IF vEsComprobanteFiscal THEN
		IF EXISTS(SELECT * FROM comm 
			  WHERE comm.idpad = pIdPad AND	comm.afipcbtetipo = pAfipCbteTipo AND comm.nroptovta = pNroPtoVta AND comm.nro = pNro AND
			        (pIdComm IS NULL OR comm.id <> pIdComm) AND
			        INDEX(vSucsFisDeMiEmpresa, comm.codambsuc) > 0)  THEN
		   RAISE EXCEPTION 'Control Fiscal: El comprobante ya existe para esta entidad.';
		END IF;
		RETURN TRUE; 
	ELSE
		IF EXISTS(SELECT * FROM ComM 
			WHERE (pIdPad is NULL OR IdPad = pIdPad) AND (comm.letra = pLetra) AND (Nro = pNro) AND (TipoCom = pTipoCom) AND
			( COALESCE(pNroPtoVta, -1) =  COALESCE(ComM.NroPtoVta, -1)  )  AND
			((pIdComm IS NULL) OR (ComM.Id <> pIdComm)) AND
			INDEX(vSucsDeMiEmpresa, ComM.codambsuc) > 0
			)  THEN
		   RAISE EXCEPTION 'El comprobante ya existe para esta entidad.';
		END IF;
		RETURN TRUE; 
	END IF;
END IF;

/* TipoUnicidad = 'F': Controla que el numero no se repita dentro en una misma entidad y una misma fecha */
IF vTipoUnicidad = 'F' THEN
	IF vEsComprobanteFiscal THEN
		IF EXISTS(SELECT * FROM comm 
			  WHERE comm.idpad = pIdPad AND	comm.afipcbtetipo = pAfipCbteTipo AND comm.nroptovta = pNroPtoVta AND comm.nro = pNro AND comm.fecha = pFecha AND 
			        (pIdComm IS NULL OR comm.id <> pIdComm) AND
			        INDEX(vSucsFisDeMiEmpresa, comm.codambsuc) > 0)  THEN
		   RAISE EXCEPTION 'Control Fiscal: El comprobante ya existe para esta entidad en esta fecha.';
		END IF;
		RETURN TRUE; 
	ELSE
		IF EXISTS(
			SELECT *
			FROM ComM 
			WHERE (pIdPad is NULL OR IdPad = pIdPad) AND (Letra = pLetra) AND (Nro = pNro) AND (TipoCom = pTipoCom) AND
			( COALESCE(pNroPtoVta, -1) =  COALESCE(ComM.NroPtoVta, -1)  )  AND ( Fecha = pFecha ) AND 
			((pIdComm IS NULL) OR (ComM.Id <> pIdComm)) AND
			INDEX(vSucsDeMiEmpresa, ComM.codambsuc) > 0
			)  THEN
		    RAISE EXCEPTION 'El comprobante ya existe para esta entidad en esta fecha.';
		END IF;
		RETURN TRUE; 
	END IF;
END IF;

/* TipoUnicidad = 'A': Controla que el numero no se repita para este tipo de comprobante ni en otros que usen el mismo talonario.                          */
/*   Caso comprobante fiscal: No se usa el talonario sino el tipo comprobante afip y el puesto de venta.                                                   */
/* 			Para discriminar entre comprobantes de venta / compra, usamos el codigo de subdiario contable (com.codctbsub)                      */
/* 			Ejemplo: Una FV codigo 001 Afip y una FC tambien codigo 001 con el mismo puesto daria repetido.                                    */
/* Para el caso de los comprobantes propios emitidos por un impresor fiscal, puede que el talonario tenga una fecha de inicio de control de numeracion,    */
/*             y como es muy dificil recuperarlo cuando el comm.idcomtal es nulo (caso win), se sigue controlando a traves del talonario como antes.       */

IF (vTipoUnicidad = 'A') AND (vEsComprobanteFiscal) AND (pTipoCai <> 'F') THEN
	IF EXISTS(SELECT * FROM comm 
		  WHERE comm.afipcbtetipo = pAfipCbteTipo AND comm.nroptovta = pNroPtoVta AND comm.nro = pNro AND
			comm.codctbsub = (select codctbsub from com where tipo = pTipoCom) AND
		        (pIdComm IS NULL OR comm.id <> pIdComm) AND
		        INDEX(vSucsFisDeMiEmpresa, comm.codambsuc) > 0)  THEN
	   RAISE EXCEPTION 'Control Fiscal: Un comprobante del mismo talonario ya existe.';
	END IF;
	RETURN TRUE; 
END IF;


/* TipoUnicidad = 'A': Controla que el numero no se repita para este tipo de comprobante ni en otros que usen el mismo talonario */
/* Caso comprobante no fiscal, IdComTal = NULL (uso desde Gestion Win) */
IF (vTipoUnicidad = 'A') AND (pIdComTal IS NULL) THEN
	/* Primero recuperamos el talonario que debio usar ese comprobante */
	SELECT ComTal.Cod, fechainiciocontrolnumeracion INTO vCodComTal, vFechaInicioControlNumeracion
	FROM ComXTal
	INNER JOIN ComTal ON ComTal.Cod = ComXTal.CodComTal
	WHERE (ComXTal.nombreambequ = pNombreAmbEqu or ComXTal.nombreambequ = '*') AND
	      (ComXTal.CodAmbSuc IS NULL or ComXTal.CodAmbSuc = pCodAmbSuc) AND
	      ComXTal.Letra = pLetra AND
	      ComXTal.TipoCom = pTipoCom AND
	      (ComXTal.NroPtoVta = pNroPtoVta or ComXTal.NroPtoVta IS NULL)
	ORDER BY 
	CASE WHEN COALESCE(TRIM(ComXTal.codambsuc), '') = '' THEN NULL
	     ELSE ComXTal.codambsuc
	END NULLS LAST, 
	ComXTal.Letra, 
	CASE ComTal.TipoNumeracion
		WHEN 'F' THEN 1
		WHEN 'E' THEN 2
		WHEN 'A' THEN 3
		WHEN 'X' THEN 4
		WHEN 'M' THEN 5
		ELSE 6
	END, 
	NroPtoVta NULLS LAST,
	ComXTal.CodComTal 
	LIMIT 1;
	IF vCodComTal IS NULL THEN
		RAISE EXCEPTION 'Debe existir un talonario para el puesto % en la sucursal %, equipo %, para % %', 
		pNroPtoVta, pCodAmbSuc, pNombreAmbEqu, pTipoCom, pLetra;
	END IF;
	/* Recupero todos los comprobantes que han numerado con el mismo talonario que este comprobante (vCodComTal)  */
	IF EXISTS (
		SELECT ComM.id as idcomm
		FROM ComXTal 
		INNER JOIN ComTal ON ComTal.Cod = ComXTal.CodComTal
		INNER JOIN ComM ON ComXTal.TipoCom = ComM.TipoCom AND 
		           ( vFechaInicioControlNumeracion IS NULL OR  ComM.fecha >= vFechaInicioControlNumeracion ) AND
		           (Comm.nombreambequ IS NULL OR ComXTal.nombreambequ  = comm.nombreambequ or ComXTal.nombreambequ = '*') AND
		           (ComXTal.CodAmbSuc IS NULL OR ComXTal.CodAmbSuc = comm.codambsuc) AND
		           ComXTal.Letra = comm.letra AND
		           (ComXTal.NroPtoVta IS NULL OR ComXTal.NroPtoVta = comm.nroptovta)
		WHERE ComM.Nro = pNro AND ComM.Letra = pLetra AND COALESCE(pNroPtoVta, -1) =  COALESCE(ComM.NroPtoVta, -1) AND
		      (pIdComm IS NULL OR ComM.Id <> pIdComm) AND
		      INDEX(vSucsDeMiEmpresa, ComM.codambsuc) > 0
		GROUP BY 1
		HAVING SUBSTRING(MIN(CASE ComTal.TipoNumeracion
					WHEN 'F' THEN '1' || ComTal.Cod
					WHEN 'E' THEN '2' || ComTal.Cod
					WHEN 'A' THEN '3' || ComTal.Cod
					WHEN 'X' THEN '4' || ComTal.Cod
					WHEN 'M' THEN '5' || ComTal.Cod
					ELSE '6' || ComTal.Cod
				     END) , 2, 50) = vCodComTal) THEN
	    RAISE EXCEPTION 'Un comprobante del mismo talonario ya existe.';
	END IF;
	RETURN TRUE; 
END IF;

/* TipoUnicidad = 'A': Controla que el numero no se repita para este tipo de comprobante ni en otros que usen el mismo talonario */
/* Caso comprobante no fiscal, IdComTal <> NULL (uso desde Gestion Web) */
IF (vTipoUnicidad = 'A') AND (pIdComTal IS NOT NULL) THEN
	/* Recuperamos los datos del talonario que uso ese comprobante */
	SELECT fechainiciocontrolnumeracion INTO vFechaInicioControlNumeracion
	FROM comtal
	WHERE comtal.id = pIdComTal;
	-- Verifica la unicidad del numero utilizado.
	IF EXISTS ( SELECT * FROM comm
		    WHERE comm.idcomtal = pIdComTal AND comm.nro = pNro AND
		          (pIdComm IS NULL OR comm.id <> pIdComm) AND
		          INDEX(vSucsDeMiEmpresa, comm.codambsuc) > 0 AND comm.fecha >= COALESCE(vFechaInicioControlNumeracion, comm.fecha) ) THEN 
	   RAISE EXCEPTION 'Un comprobante del mismo talonario ya existe.';
	END IF;
	RETURN TRUE; 
END IF;

/* TipoUnicidad = 'U': Controla que el numero no se repita considerando las entidades con la misma C.U.I.T. */
IF (vTipoUnicidad = 'U') THEN
	SELECT Cuit INTO vCuit FROM Pad WHERE Id = pIdPad;
	IF vEsComprobanteFiscal THEN
		IF EXISTS(SELECT * FROM comm 
		          INNER JOIN pad ON comm.idpad = pad.id
			  WHERE pad.cuit = vCuit AND comm.afipcbtetipo = pAfipCbteTipo AND comm.nroptovta = pNroPtoVta AND comm.nro = pNro AND
			        (pIdComm IS NULL OR comm.id <> pIdComm) AND
			        INDEX(vSucsFisDeMiEmpresa, comm.codambsuc) > 0)  THEN
		   RAISE EXCEPTION 'Control Fiscal: El comprobante ya existe para el mismo CUIT.';
		END IF;
		RETURN TRUE; 
	ELSE
		IF EXISTS ( SELECT *
			    FROM ComM
			    INNER JOIN Pad ON ComM.IdPad = Pad.Id
			    WHERE ComM.Letra = pLetra AND ComM.Nro = pNro AND 
			          ComM.TipoCom = pTipoCom AND Pad.Cuit = vCuit AND
			          COALESCE(pNroPtoVta, -1) =  COALESCE(ComM.NroPtoVta, -1) AND
			          (pIdComm IS NULL OR ComM.Id <> pIdComm) AND
			          INDEX(vSucsDeMiEmpresa, ComM.codambsuc) > 0) THEN
		    RAISE EXCEPTION 'El comprobante ya existe para el mismo CUIT.';
		END IF;
		RETURN TRUE;
	END IF;
END IF;

/* Si llego aqui se trata de un TipoUnicidad desconocido. */
RAISE DEBUG 'Entro al else final de Store Procedure VERI_UNI';
RETURN TRUE;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION public.veri_uni(integer, character varying, integer, character varying, integer, integer, character varying, character varying, character varying, timestamp without time zone, integer, integer)
  OWNER TO abacouser;



-- =================================

SELECT aaversion('420.01','420.00');

-- =================================
